package xyz.ziduo.lapcounter;

import static xyz.ziduo.lapcounter.utils.Utils.uartInit;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbRequest;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;

import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.Semaphore;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import xyz.ziduo.lapcounter.utils.Utils;

public class RunningActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "LC: " + MainActivity.class.getSimpleName();
    private static final String ACTION_USB_PERMISSION = "xyz.ziduo.lapcounter.USB_PERMISSION";

    // 应用数据根目录
    // 跑步数据根目录
    // 跑步数据按年份分成子目录放入该目录下，跑步数据文件按日期命名，放入对应年份目录中
    // 2024 > 12-30.txt
    private static final String FILE_RUN_DATA_DIR = "data/rundata";
    private UsbManager usbManager;
    private PendingIntent permissionIntent;

    private TextView tv_lap_count, tv_alert, tv_mileage, tv_conn, tv_save, tv_pace_lap, tv_pace_avr;

    private int lapLength = 0;
    private boolean isRoundMode = false;
    private boolean isLapPlanCheck = false;
    private int lapPlanNum = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_running);

        usbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
        int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE : 0;
        permissionIntent = PendingIntent.getBroadcast(this, 10, new Intent(ACTION_USB_PERMISSION), flags);

        // 注册usb事件接收器
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        registerReceiver(usbReceiver, filter);

        initView();

        Intent intent = getIntent();
        lapLength = intent.getIntExtra("lapLength", 0);
        isRoundMode = intent.getBooleanExtra("isRoundMode", false);
        isLapPlanCheck = intent.getBooleanExtra("isLapPlanCheck", false);
        lapPlanNum = intent.getIntExtra("lapPlanNum", 0);

        tickHandler = new TickHandler(this);
    }

    private Drawable runningAlertErr, runningAlertGood, runningAlertNormal;
    private void initView() {
        Resources resources = getResources();
        runningAlertErr = resources.getDrawable(R.drawable.v_running_alert_err);
        runningAlertGood = resources.getDrawable(R.drawable.v_running_alert_good);
        runningAlertNormal = resources.getDrawable(R.drawable.v_running_alert_normal);

        tv_lap_count = findViewById(R.id.tv_lap_count);
        tv_alert = findViewById(R.id.tv_alert);
        tv_mileage = findViewById(R.id.tv_mileage);
        tv_conn = findViewById(R.id.tv_conn);
        tv_save = findViewById(R.id.tv_save);
        tv_pace_lap = findViewById(R.id.tv_pace_lap);
        tv_pace_avr = findViewById(R.id.tv_pace_avr);

        tv_conn.setOnClickListener(this);
        tv_save.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        if(isFastClick()) return; // 如果是快速点击则不进行响应
        if(view.getId() == R.id.tv_conn && !isUsbConnected) {
            requestPermission();
        } else if(view.getId() == R.id.tv_save) {
            saveRunDataAndExit();
        }
    }

    private volatile boolean isUsbConnected = false;
    // Usb数据读取线程
    private UsbReadThread mUsbReadThread;
    private class UsbReadThread extends Thread {
        private volatile boolean stop = false;
        private UsbDevice usbDevice;
        private UsbDeviceConnection usbDeviceConnection;
        private UsbInterface usbInterface;
        private int usbRequestCount = 20;
        private UsbRequest[] requestSet = new UsbRequest[usbRequestCount];
        private ByteBuffer[] readByteBufferSet = new ByteBuffer[usbRequestCount];
        private Semaphore semaphore = new Semaphore(1);
        private boolean isNullUsb = false;
        private UsbEndpoint usbEndpoint_E;
        private UsbEndpoint usbEndpoint_F;
        private int maxPacketSize;
        private StringBuilder allBytes = new StringBuilder();
        private StringBuilder currentLine = new StringBuilder();
        private byte[] longestProcessedBytes = null;
        private boolean hasExtracted = false;
        public UsbReadThread(UsbDevice device) {
            usbDevice = device;
        }
        public long tickTime = 0;
        // 停止线程
        public void doStop() {
            stop = true;
        }
        /**
         * 用户需知：本方法使用了 https://github.com/Hny0305Lin/CH34x/blob/master/CH34xUARTDriver.java，中的部分代码
         *      该代码为 浩瀚银河 开发，二次开发请尊重浩瀚银河和上游作者版权，添加"浩瀚银河"署名和添加醒目文字以让用户知请使用反编译源码
         *      代码许可：https://github.com/Hny0305Lin/LICENSE/blob/main/LICENSE
         * 控制传输
         * 适用于CH34x芯片
         */
        @Override
        public void run() {
            super.run();
            UsbInterface usbInterface;
            UsbDeviceConnection openDevice;
            boolean z;
            if (usbDevice == null) {
                return;
            }
            if (this.usbDeviceConnection != null) {
                if (this.usbInterface != null) {
                    this.usbDeviceConnection.releaseInterface(this.usbInterface);
                    this.usbInterface = null;
                }
                this.usbDeviceConnection.close();
                this.usbDevice = null;
                this.usbInterface = null;
            }
            if (usbDevice != null) {
                int i = 0;
                while (true) {
                    if (i >= usbDevice.getInterfaceCount()) {
                        usbInterface = null;
                        break;
                    }
                    UsbInterface usbInterface2 = usbDevice.getInterface(i);
                    if (usbInterface2.getInterfaceClass() == 255 && usbInterface2.getInterfaceSubclass() == 1 && usbInterface2.getInterfaceProtocol() == 2) {
                        usbInterface = usbInterface2;
                        break;
                    }
                    i++;
                }
            } else {
                usbInterface = null;
            }
            UsbInterface usbInterface3 = usbInterface;
            if (usbDevice == null || usbInterface3 == null || (openDevice = usbManager.openDevice(usbDevice)) == null || !openDevice.claimInterface(usbInterface3, true)) {
                return;
            }
            this.usbDevice = usbDevice;
            this.usbDeviceConnection = openDevice;
            this.usbInterface = usbInterface3;
            if (usbInterface3 == null) {
                z = false;
            } else {
                for (int numberOfUSBPotrs = 0; numberOfUSBPotrs < usbInterface3.getEndpointCount(); numberOfUSBPotrs++) {
                    UsbEndpoint endpoint = usbInterface3.getEndpoint(numberOfUSBPotrs);
                    if (endpoint.getType() == 2 && endpoint.getMaxPacketSize() == 32) {
                        /*
                         * getDirection()方法为返回端点的方向。
                         * 如果方向是主机到设备，则返回{@link UsbConstants#USB_DIR_OUT}，如果方向是设备到主机，则返回{@link UsbConstants#USB_DIR_IN}。
                         *
                         * @see UsbConstants#USB_DIR_IN     128
                         * @see UsbConstants#USB_DIR_OUT    0
                         * @返回端点的方向
                         */
                        if (endpoint.getDirection() == UsbConstants.USB_DIR_IN) {
                            this.usbEndpoint_E = endpoint;
                        } else {
                            this.usbEndpoint_F = endpoint;
                        }
                        this.maxPacketSize = endpoint.getMaxPacketSize();
                    } else {
                        endpoint.getType();
                    }
                }
                z = true;
            }
            if (z) {
                // 1 表示已连接
                tickHandler.sendEmptyMessage(1);
                if (this.isNullUsb) {
                    return;
                }
                this.isNullUsb = true;
            }

            for (int i = 0; i < usbRequestCount; ++i) {
                /*
                 * 读取数据，初始化32位到缓冲区
                 *
                 * UsbRequest#queue()：将请求排队以在其端点上发送或接收数据。
                 *
                 * <p>对于OUT端点，给定的缓冲区数据将在端点上发送。对于IN端点，端点将尝试将给定数量的字节读取到指定的缓冲区中。
                 * 如果排队操作成功，则返回true。结果将通过{@link UsbDeviceConnection#requestWait}返回
                 * </p>
                 * @param buffer 该缓冲区包含要写入的字节或用于存储读取结果的位置。
                 *               位置和数组偏移将被忽略，并假定为0。极限和容量将被忽略。
                 *               一旦请求{@link UsbDeviceConnection#requestWait()得到处理}，位置将被设置为读/写的字节数。
                 * @param length 要读取或写入的字节数。在{@value Build.VERSION_CODES#P}之前，
                 *               大于16384字节的值将被截断为16384。在API {@value Build.VERSION_CODES#P}中及之后的任何长度值都是有效的。
                 * @return true，如果排队操作成功
                 * @deprecated 已过时， {@link UsbRequest#queue(ByteBuffer)} 代替。
                 */
                UsbRequest request = new UsbRequest();
                request.initialize(usbDeviceConnection, usbEndpoint_E);
                requestSet[i] = request;
                readByteBufferSet[i] = ByteBuffer.allocate(maxPacketSize * 4);
                requestSet[i].queue(readByteBufferSet[i], 32);
            }
            // 初始化UART
            if(!uartInit(usbDeviceConnection)) {
                Log.e(TAG, "uartInit failed.");
                return;
            }
            int count = 0;

            StringBuilder stringBuilder = new StringBuilder();
            byte[] lineBuff = new byte[1024];
            int lineInd = 0;

            root:
            while (!stop) {
                do {
                    //没有打开USB则返回
                    if (!isNullUsb) break;
                } while (usbEndpoint_E == null && !stop);

                for (int i = 0; i < usbRequestCount; ++i) {
                    //等待USB
                    UsbRequest usbRequest = this.usbDeviceConnection.requestWait();
                    UsbRequest usbRequestI = requestSet[i];

                    //如果USB已关闭且计数器超过20、如果请求为空或不匹配，则跳出循环，重置计数器
                    if (!isNullUsb && count++ > 20) {
                        Log.v(TAG, "USB已经关闭");
                        break root;                             //跳出循环
                    }
                    if (usbRequest == null) break;
                    if (usbRequest != usbRequestI) {
                        continue;
                    }
                    count = 0;

                    //获取当前请求的字节缓冲区和长度
                    byte[] temp = readByteBufferSet[i].array();
                    int tempLength = readByteBufferSet[i].position();
                    if (tempLength > 0) {

                        try {
                            //获取
                            semaphore.acquire();

                            //处理字节数组
                            for (int j = 0; j < tempLength; j++) {
                                allBytes.append(String.format("%02x", temp[j] & 0xFF)); //转换为小写十六进制
                                if (temp[j] == 0x0a) { // 检查是否为换行符
                                    if (currentLine.length() > 0) {
                                        //使用UTF-8编码转换
                                        byte[] processedBytes = hexStringToByteArray(currentLine.toString());
                                        if (longestProcessedBytes == null || processedBytes.length > longestProcessedBytes.length) {
                                            longestProcessedBytes = processedBytes; // 更新为最长的字节数组
                                            hasExtracted = true;
                                        }
                                        String line = new String(hexStringToByteArray(currentLine.toString()), StandardCharsets.US_ASCII);
                                        //Log.e(TAG, line + " " + line.length());
                                        if(line.length() == 11) {
                                            long tm = System.currentTimeMillis();
                                            if(tm - tickTime > 1000) {
                                                tickHandler.sendEmptyMessage(2); // 表示有人经过
                                                tickTime = tm;
                                            }
                                        }
                                        currentLine.setLength(0);
                                    }
                                } else {
                                    if (allBytes.length() > 0) {
                                        currentLine.append(String.format("%02x", temp[j] & 0xFF));
                                    }
                                }
                            }

                            //检查并输出最后一行（如果存在）
                            if (currentLine.length() > 0) {
                                byte[] processedBytes = hexStringToByteArray(currentLine.toString());
                                if (longestProcessedBytes == null || processedBytes.length > longestProcessedBytes.length) {
                                    longestProcessedBytes = processedBytes; // 更新为最长的字节数组
                                    hasExtracted = false;
                                }
                                String line = new String(processedBytes, StandardCharsets.US_ASCII);
                            }

                            //清空allBytes以避免累积
                            allBytes.setLength(0);
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            semaphore.release();
                        }
                    }
                    // 将请求重新排队
                    requestSet[i].queue(readByteBufferSet[i], 32);
                }
            }
            if(usbDeviceConnection != null) {
                usbDeviceConnection.releaseInterface(usbInterface);
                usbDeviceConnection.close();
            }
        }
    }

    private TestThread testThread;
    private class TestThread extends Thread {
        private boolean stop = false;
        @Override
        public void run() {
            super.run();
            long startTime = System.currentTimeMillis();
            while (!stop) {
                tickHandler.sendEmptyMessage(2);
                try {
                    Thread.sleep((long)(3000 + 1000 * Math.random()));
                } catch (Exception e) {}
            }
        }
        public void doStop() {
            stop = true;
        }
    }
    private int lapCount = 0; // 圈数
    private int tickCount = 0; // 传感器每次传来tick数据就+1
    private long runStartTime = 0; // 开始时间
    private long runTime = 0; // 总时间
    private long lapStartTime = 0; // 本圈开始时间
    private long lapTime = 0; // 本圈时间

    private ArrayList<Double> runPaceList = new ArrayList<>();
    private long runAllTime = 0; // 跑步总时间，单位毫秒

    // USB 数据处理 Handler
    private TickHandler tickHandler;
    private static class TickHandler extends Handler {
        private WeakReference<RunningActivity> hostWeakReference;
        private int lapLength = 0; // 圈长
        private boolean isRoundMode = false; // 是否是来回模式
        private boolean isLapPlanCheck = false; // 是否勾选计划圈数
        private int lapPlanNum = 0; // 计划圈数

        public TickHandler(RunningActivity hostActivity) {
            hostWeakReference = new WeakReference<>(hostActivity);
            this.lapLength = hostActivity.lapLength;
            this.isRoundMode = hostActivity.isRoundMode;
            this.isLapPlanCheck = hostActivity.isLapPlanCheck;
            this.lapPlanNum = hostActivity.lapPlanNum;
        }
        @Override
        public void handleMessage(@NonNull Message msg) {
            RunningActivity host = hostWeakReference.get();
            super.handleMessage(msg);
            if(null != host) {
                int what = msg.what;
                if(what == 1) { // 表示已连接
                    host.tv_alert.setBackground(host.runningAlertNormal);
                    host.isUsbConnected = true;
                } else if(what == 2) { // 表示收到发达来的tick数据
                    if(host.runStartTime == 0) { // 第一次收到tick数据
                        host.tickCount = 1;
                        host.runStartTime = System.currentTimeMillis();
                        host.lapCount = 0;
                        host.runTime = 0;
                        host.lapStartTime = host.runStartTime;
                        host.lapTime = 0;
                        host.tv_lap_count.setText(String.valueOf(host.lapCount));
                        host.tv_pace_lap.setText(Utils.paceToString(0));
                        host.tv_pace_avr.setText(Utils.paceToString(0));
                    } else {
                        ++ host.tickCount;
                        if(isRoundMode) { // 如果是来回模式
                            if(host.tickCount % 2 == 1) {
                                ++ host.lapCount;
                                long time = System.currentTimeMillis();
                                host.runTime = time - host.runStartTime;
                                host.lapTime = time - host.lapStartTime;
                                host.lapStartTime = time;
                                double paceLap = Utils.countPace(host.lapTime, lapLength);
                                int runLength = lapLength * host.lapCount;
                                double paceAvr = Utils.countPace(host.runTime, runLength);
                                Log.e(TAG, "paceLap" + paceLap);
                                Log.e(TAG, "paceAvr" + paceAvr);

                                host.runPaceList.add(paceLap);
                                host.runAllTime = host.runTime;
                                host.tv_lap_count.setText(String.valueOf(host.lapCount));
                                if(host.lapCount >= this.lapPlanNum)
                                    host.tv_lap_count.setTextColor(Color.GREEN);
                                host.tv_pace_lap.setText(Utils.paceToString(paceLap));
                                host.tv_pace_avr.setText(Utils.paceToString(paceAvr));
                                String runLengthStr = String.format("%.2f km", runLength / 1000.0);
                                host.tv_mileage.setText(runLengthStr);
                            }
                        } else {
                            ++ host.lapCount;
                            long time = System.currentTimeMillis();
                            host.runTime = time - host.runStartTime;
                            host.lapTime = time - host.lapStartTime;
                            host.lapStartTime = time;
                            double paceLap = Utils.countPace(host.lapTime, lapLength);
                            int runLength = lapLength * host.lapCount;
                            double paceAvr = Utils.countPace(host.runTime, runLength);
                            host.runPaceList.add(paceLap);
                            host.runAllTime = host.runTime;
                            host.tv_lap_count.setText(String.valueOf(host.lapCount));
                            if(host.lapCount >= this.lapPlanNum)
                                host.tv_lap_count.setTextColor(Color.GREEN);
                            host.tv_pace_lap.setText(Utils.paceToString(paceLap));
                            host.tv_pace_avr.setText(Utils.paceToString(paceAvr));
                            String runLengthStr = String.format("%.2f km", runLength / 1000.0);
                            host.tv_mileage.setText(runLengthStr);
                        }
                    }
                }
            }
        }
    }

    private String countRunDataString() {
        int runLength = lapLength * runPaceList.size();
        StringBuilder sb = new StringBuilder();
        sb.append(runLength).append(" ").append(runAllTime).append("\n");
        for(int i=0; i< runPaceList.size(); ++i) {
            sb.append(" ").append(runPaceList.get(i)).append("\n");
        }
        return sb.toString();
    }

    // USB设备权限广播接收器
    private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if(ACTION_USB_PERMISSION.equals(action)) {
                synchronized (this) {
                    if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                        if(device != null) {
                            // 启动 usb 数据读取线程，开始读取数据
                            mUsbReadThread = new UsbReadThread(device);
                            mUsbReadThread.start();
                        };
                    } else {
                        Log.e(TAG, "USB permission denied.");
                        Utils.dplAlert(RunningActivity.this, "需要USB访问权限");
                    }
                }
            }
        }
    };

    // 请求USB设备权限
    private void requestPermission() {
        // 获取已连接的USB设备列表
        HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
        Log.e(TAG, "dev num = " + deviceList.size());
        for(UsbDevice device : deviceList.values()) {
            usbManager.requestPermission(device, permissionIntent);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(usbReceiver);
        if(mUsbReadThread != null) mUsbReadThread.doStop();
    }

    // 跑步数据块格式： 块首行：length pace_avr，length跑步总长，单位米，pace_avr为平均配速，单位秒
    // 块数据行：行首一个空格，然后跟本圈配速，单位秒
    // 配速即跑一公里所用时间
    // 保存跑步数据
    // runData：跑步数据，date：跑步时间
    private void saveRunDataAndExit() {
        if(lapCount == 0) return;
        // 将数据写入文件
        SimpleDateFormat format = new SimpleDateFormat("yyyy MM-dd");
        String[] arr = format.format(new Date()).split(" ");
        String yearStr = arr[0];
        String dateStr = arr[1];
        String fileName = dateStr + ".txt";
        String dataStr = countRunDataString();

        if(Utils.writeFile(RunningActivity.this, FILE_RUN_DATA_DIR + "/" + yearStr,
                fileName, dataStr, true)) { // 保存成功
            Intent intent = new Intent(this, MainActivity.class);
            //intent.putExtra("runDataStr", dataStr);
            setResult(RESULT_OK, intent);
            finish();
        } else { // 保存失败
            Intent intent = new Intent(this, MainActivity.class);
            setResult(Activity.RESULT_FIRST_USER, intent);
            finish();
        }
    }

    // 按钮的点击时间
    private long mClickTime;
    // 判断是否为快速点击
    private boolean isFastClick() {
        long clickTime = System.currentTimeMillis();
        if(clickTime - mClickTime > 200) { // 点击间隔超 200ms 则认为不是快速点击
            mClickTime = clickTime;
            return false;
        }
        return true;
    }

    // 辅助方法：将十六进制字符串转换为字节数组
    private byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }
}